#include "../include/RedisPool.h"

//像这种全局变量或者是定义某一个类或者使用单例对象时，变量应该写在哪个位置？如何确定作用域的范围？
pthread_once_t RedisPool::_once = PTHREAD_ONCE_INIT;
RedisPool* RedisPool::_pInstance = nullptr;
// static pthread_once_t _once = 0;  // 线程安全
// // static std::mutex mtx;
int RedisPool::conn_num = 4;
// static std::string ip = "127.0.0.1";
// static int port = 6379;
redisContext** RedisPool::conn_pool = nullptr;
int* RedisPool::conn_flag = nullptr;
// static int empty_num = 4;
// static int current_conn = 0;

RedisPool& RedisPool::getInstance() {
    pthread_once(&_once, init);
    return *_pInstance;
}

void RedisPool::init() {
    _pInstance = new RedisPool;
    atexit(destroy);
}

void RedisPool::destroy() {
    for (int i = 0; i < conn_num; ++i) {
        if (conn_pool[i] != NULL) {
            redisFree(conn_pool[i]);
            conn_pool[i] = NULL;
        }
    }
    delete[] conn_pool;

    if (conn_flag != NULL) {
        delete[] conn_flag;
        conn_flag = NULL;
    }
    if (_pInstance) {
        delete _pInstance;
        _pInstance = nullptr;
    }
}

RedisPool::RedisPool() {
    conn_pool = new redisContext*[conn_num];
    if (conn_pool == NULL) {
        //LogInfo("conn_pool is null");
        return;
    }

    conn_flag = new int[conn_num];
    if (conn_flag == NULL) {
        //LogInfo("conn_flag is null");
        return;
    }

    for (int i = 0; i < conn_num; ++i) {
        // !. 待改回： conn_pool[i] = redisConnect(ip.c_str(), port);
        conn_pool[i] = redisConnect("127.0.0.1", 6379);
        if (conn_pool[i] == NULL || conn_pool[i]->err) {
            //LogInfo("conn_pool:%d is null", i);
            return;
        }

        conn_flag[i] = 0;
    }

    empty_num = conn_num;
    current_conn = 0;
}

RedisPool::~RedisPool() {}

redisContext* RedisPool::get_conn(int id) {
    if (empty_num == 0) {
        return NULL;
    }

    mtx.lock();

    while (conn_flag[current_conn] != 0) {
        current_conn = (current_conn + 1) % conn_num;
    }

    conn_flag[current_conn] = 1;
    --empty_num;
    id = current_conn;
    current_conn = (current_conn + 1) % conn_num;

    mtx.unlock();

    return conn_pool[id];
}

void RedisPool::put_conn(int id) {
    if (id < conn_num && id >= 0) {
        mtx.lock();

        conn_flag[id] = 0;
        ++empty_num;

        mtx.unlock();
    }

    return;
}
